home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 19
/
Aminet 19 (1997)(GTI - Schatztruhe)[!][Jun 1997].iso
/
Aminet
/
demo
/
mag
/
trash3_2.lha
/
fuentes
/
Phornee_Dum4k.lha
/
Phornee.Dum_de_4k.DOC
Wrap
Text File
|
1991-10-01
|
30KB
|
704 lines
*************************************** ©PHORNEE/GOBLINS ********************************************
En espaÑol, ¡joder!... ya era hora....
Nota: Si alguien no entiende eso de "blitter", "copper", etc, que se aguante y se vaya a jugar
con su videoconsola 486 (para su vergüenza... el hermano pequeño del pentium.)
Como hacer un laberinto 3D tipo Doom-Wolfstein en 5 minutos... (je,je)
PRELIMINAR
==========
Para tratar números decimales con maxima eficiencia, es recomendable trabajar con un sistema de
coma fija (16:16) (bits, claro!)
Sin & Cos: Despues de todo el rollo, veras que no son necesarias tablas de senos.
Tan & Cotan: 16 entero 16 decimales
Cosec & Sec: 16 entero 16 decimales
Angulos: 0-2¶ ===> 0-2047
Así evitaré toda llamada al S.O para manejo de coma flotante, y manejar decimales será casi tan rápido
como hacerlo con enteros. No se harán llamadas a la mathieeedoubbas.library.
Ademas, con la inclusión de tablas precalculadas de las razones trigonométricas, se evitarán llamadas
a la mathieeedoubtrans.library.
Aqui se exponen los algoritmos de proyección de rayos... el dibujo en pantalla de las paredes va de
vuestra cuenta.
Se intentará que los accesos a memoria sean mínimos, y la inmensa mayoría de las instrucciones serán
operaciones con registros.
Asimismo, será preferible usar codigo especial para 68020+, ya que esto acelera los acessos a tablas
Longword & Word.
Ademas, dado el nivel de anidamiento de los algoritmos y comparaciones, sería MUY conveniente usar
algún tipo de macros para dotar al ensamblador de WHILEs,UNTILs,REPEATs,IF-THEN-ELSEs,etc..
(Lo mejor es que utilices el "PhDefs" que yo hize a tal efecto. ¿Quién necesita el 'C' ahora?).
NUESTRO LABERINTO.....................
=======================================
Visión de la Planta del laberinto.
Cada bloque es de 256x256 puntos.
. . . . . .
. . . . . .
--------........--------........----------------
|bloque|........|bloque|........|bloque||bloque|
| |........| |........| || | ... Los bloques marcados con puntos, se supone
| 0,3 |........| 2,3 |........| 4,3 || 5,3 | que son bloques "vacios", sin pared.
--------........--------........----------------
........--------................--------........ Cada bloque de pared, tiene cuatro valores,
........|bloque|................|bloque|........ uno para cada dirección (arriba,derecha...)
........| |................| |........ ... Esto permitirá que un bloque sólido,tenga cada
........| 1,2 |................| 4,2 |........ pared de una textura diferente. Asi pues,
........--------................--------........ cada bloque solido tiene 4 paredes.
................................................
................................................ Mi laberinto será de 256*256 bloques, donde
................................................ cada bloque es de 256*256 puntos (<>pixeles)
................................................ ... Asi cabe todo en una matriz bidimensional
................................................ de (Word*Word).
--------........--------........................
|bloque|........|bloque|........................ El observador(*) estará en una coord.(Ox,Oy)
| |........| del *|........................ ... (0..65535,0..65535), por lo cual estará sito
| 0,0 |........|observ|........................ en el bloque (Ox/256,Oy/256).
--------........--------........................
¿Que coño debemos dibujar en la pantalla?.
Debemos dibujar, lo que vea el observador.El observador ó cámara, además de estar en una determinada
posición (Ox,Oy), estará mirando hacia una dirección en concreto.
Esta dirección la llamaremos ß. ß es un ángulo. Además de eso, tenemos que definir el espectro o
'diafragma' de visión del observador. La cámara ve en un entorno alrededor de su direccion de
mirada. Este "diafragma" lo llamaremos Angß (Incremento de ß). Un ejemplo:
/n DIRECCION DE ANGULOS
/ó * = observador 90°
/i |
/c ...... origen de los ángulos (ángulo 0) |
/c 180° ------+------ 0°
/e / dirección donde mira el observador. |
/r |
/i \ ß ß ángulo que define la dirección de mirada en mapa 2D. 270°
/D \
*................
Aqui trato los angulos en grados (°), para aclararme mejor, pero en implementación, usare 2048
divisiones para 2¶ radianes (360°).
Imaginemos que ß es igual a 0 (Mirando completamente a la derecha del mapa 2D), y que Angß vale 45°:
...................
/....................
/.....................
/...................... ... La región que el observador "ve", es la marcada
/.\.Angß................ con puntos. Asi pues, es la zona delimitada por
/...\.................... los ángulos ß-Angß y ß+Angß
*-------------------- ß... ...
\.../....................
\./.Angß................ PLANTA DEL LABERINTO
\...................... ... ©---------------------- EJE X
\..................... |##############
\.................... |#.#......#..
................... |#.#.####.#. © representa el eje Y saliendo
|#.......... desde la pantalla.
. |######.###. # son bloques sólido
. |...........
. | . son "pasillos" (ausencia de
EJE Z bloque solido)
COMO PROYECTAR EN PSEUDO 3D
===========================
Aqui comienza el cotarro. El rollo está en que las paredes sólo giran en el eje Y. Asi pues, como
todas las paredes son verticales si hallamos si una pared se ve o no para una Y generica, ya sabremos
si se ve o no para todas sus Ys.
Asi pues, si efectuamos la visión de la planta del laberinto (Olvidandonos asi de la 3D), y hacemos
los calculos sin tener en cuenta la Y, todo se simplifica un montón, y luego no será dificil plasmarlo
en 3D en la pantalla.
A partir de ahora, y hasta que no se diga lo contrario, estamos hablando del mapa en 2D.
Consideremos que la pantalla es de 320 puntos de anchura. Si trazamos 320 rayos horizontales desde el
observador hacia el laberinto, y anotamos con qué pared choca, obtendríamos un vector lineal, que nos
diría que bloque exactamente se ve en cada X fisica de la pantalla. Fijate un poco, y verás que para
una columna en concreto de la pantalla, sólo se ve una pared. Y esa pared, deberá ser precisamente de
la textura que hemos encontrado para esa X, al trazar el rayo. Hasta aqui parece que no va a ser
muy dificil. Pero sigamos...
Ahora, consideremos que nuestra pantalla es de NumDiv pixeles de ancho. Para cubrir la pantalla
entera, necesitaremos lanzar (en principio... ya veremos, ya...) NumDiv rayos. Estos rayos deben
cubrir todo el espectro de vision o diafragma del observador, asi es que deberan ir desde ß-Angß
hasta ß+Angß. Llamemos ß0 a ß-Angß. Hallamos la diferencia de cada uno de los NumDiv ángulos entre
si, y lo llamaremos ðß (incremental de ß). ðß = [(ß+Angß) - (ß-Angß)] / NumDiv = 2*Angß / NumDiv
El primero de los rayos a trazar, es ß0. El segundo es ß0+ðß. El tercero es ß0+2*ðß...ß0+3*ðß...
... ß+(NumDiv-1)*ðß. Todos ellos, son ángulos a trazar. Asi pues, para cada columna de la pantalla,
lanzaremos un rayo.
Este sistema de trazar rayos con divisiones angulares, ocasionará seguramente una deformación
cilindrica en los extremos horizontales de la pantalla. Si queda mal, habra que arreglarlo haciendo
una división lineal del plano de proyección. (Ayer hize los calculos en clase de Redes, porque me
aburría... Los pondré al final, si no queda bien con ángulos). Pero bueno, así es como ve el ojo
real, lo que pasa es que no percibimos la deformación, porque vemos casi exclusivamente con el centro
del ojo, donde la deformación es mínima. Bueno, creo que estoy divagando otra vez...
MEJORA
======
¡¡Revelacion!!. Ya no necesitaremos trazar los 320 rayos.Lanzaremos el rayo inicial,ß0 para encontrar
la primera pared.(La del extremo izquierdo del campo de visión). Pero cuando hayamos encontrado esa
pared,podemos lanzar otro rayo directamente a la esquina final de la pared (øN). Este øN es el angulo
real a la esquina final de la pared, y no tiene por que coincidir con ninguno de los ß (es más,
seguro que no coincide). Recordemos que tenemos un número finito de ßs.(P.ej: 320).Si el rayo lanzado
choca realmente con la pared antes encontrada,ya sabemos que entre el angulo del rayo inicial, y el
segundo rayo a la esquina, SOLO hay una pared,(ESA pared),y no necesitaremos lanzar mas rayos para esa
pared,ahorrandonos bastantes operaciones.
Si el segundo rayo (a la esquina) no chocase con nuestra pared, sino con otra mas cercana al observador
,se podria hacer un tratamiento recursivo similar para esta nueva pared, y devolver el principio de
esta segunda pared, como final de la primera.
Luego se lanzaría otro rayo buscador, para encontrar la siquiente pared a la derecha. Ese rayo seria
aquel de la serie ß, posterior al øN real de la última pared.
Asi pues, devolveremos como resultado una lista. Cada nodo de la lista contendrá la información
suficiente para dibujar una pared (o cacho de pared), en orden de izquierda a derecha. La estructura
de todas las paredes, o pedazos de pared, será la de un trapecio tumbado.
ß Inic ß Final Estos ßs son dos de los "320" ßs.
. .
. .
. .
. /| ...
. / | .
. / | .
. / | .
./ | .
...| | .
AP . | | AP Estas AP (Alturas proyectadas) son función
Inicial ...| | Final directa de las Zs de los extremos de la pared.
\ | .
\ | .
\ | .
\ | .
\| ...
--------------------------------
| ß Inicial | ß Final |
|---------------+--------------|
| Z Inicial | Z Final |
|---------------+--------------|
| InicioTextura | FinalTextura |
|---------------+--------------|
| BloqueX | BloqueY |
----------------+---------------
| Dirección |
-----------------
**************
*PSEUDOCODIGO*
**************
Main
¯¯¯¯
ø=0; /*Mirando a la derecha*/
Angß=255; /*45°*/
ß=ø-Angß; /*ß=ß0*/
Punt=0; /*Primer nodo*/
DO_WHILE
TrazarBuscador(ß); /*Encuentra pared e inicializa Pared(Punt)*/
ß=TrazarVerificador(&Pared[Punt]); /*Completa Pared*/
ß=ß+ðeltaß
WHILE ß<ø+Angß;
Punt=Punt-1;
Dß=ø+Angß-Pared[Punt].ßInicial;
DP=Pared[Punt].ßFinal-Pared[Punt].ßInicial;
DZ=Pared[Punt].ZFinal-Pared[Punt].ZInicial;
Pared[Punt].ZFinal=Dß*DZ/DP
Pared[Punt].ßFinal=ø+Angß;
COMO PROYECTAR UN RAYO GENERICO
===============================
Vamos a dividir el espectro de ángulos en ocho sectores (octantes):
90° DIRECCIONES DE CHOQUE
135° | 45°
\ 3 | 2 / Arriba=0
\ | / |
4 \ | / 1 |
\|/ |
180°-------------+------------- 0° Izqui=3 ---------+--------- Derecha=1
/|\ |
5 / | \ 8 |
/ | \ |
/ 6 | 7 \ Abajo=2
225° | 315°
270°
Los valores HAD (Exahustivo Horizontal/ Arriba/ Derecha) son bits usados para mayor rapidez en
condiciones de comparación con octantes.
H vale 1, si el coseno es mayor que el seno.(MSB2=MSB3)
A vale 1, si el seno es positivo. (MSB1=0)
D vale 1, si el coseno es positivo. (MSB1=MSB2)
VALORES DE HAD VALORES DE MSB DE ANGULOS (rango 2048) LSB=x=8 bits
90° |
135° | 45° \ 010x | 001x /
\ 010 | 011 / \ | /
\ | / \ | /
110 \ | / 111 011x \ | / 000x
\|/ \|/
180°-------------+------------- 0° ------------------+----------------
/|\ /|\
100 / | \ 101 100x / | \ 111x
/ | \ / | \
/ 000 | 001 \ / | \
225° | 315° / 101x | 110x \
270° |
Ahora tomemos un rayo en particular. Será un rayo con dirección ø. Debemos seguir con
atención su recorrido para ver con que pared choca. (Si se hace recursivamente, sería trivial hacer
texturas especulares. ¡Ostias, que guapo!. Ya veremos, ya...). Para ello, testearemos todos los
bloques del laberinto que el rayo cruza, empezando desde el bloque en que se encuentra el Observador.
Cuando encontremos el primer bloque de ésta trayectoria, que es sólido, habremos tocado pared.
El bloque inicial,como ya hemos dicho,será el (Ox/256,Oy/256).Lo llamaremos B = (BOx,BOy).
Lo primero que debemos saber,es por qué lado (de los cuatro) de dicho bloque, sale el rayo...
LADO VERTICAL DEL BLOQUE (BOx,BOy) Como vamos a recorrer los bloques uno a uno desde el
---------------------------- / bloque del *, necesitamos trazar una de las dos
L | . | / trayectorias posibles, ya que sólo una de ellas es
A | . |/ adecuada.
D | OffY . y # o#
O | . ry o o
| . r y <= sen(ø)*r oo ó bien oo "o" es el rayo
H | . r y o o
O | Offx .r y *o *
R |.....................*xxxxy........... Donde * es el bloque del observador, y # es el primer
I | ^ | bloque impactado por el rayo. En el primer caso, el
Z | cos(ø)*r| rayo choca con la pared de abajo del bloque. En el
---------------------------- segundo, choca con la pared izquierda.
Para diferenciar las dos trayectorias, debemos saber por cual de los cuatro lados dentro del bloque
de la camara, sale el rayo hacia su destino. Es mas... sólo nos interesará saber, si sale horizontal
o verticalmente. Tenemos pues, el rayo de angulo ø, y la posicion del observador (Ox,Oy).
Hallemos el offset del observador dentro de su bloque:
OffsetX= Ox-BOx*256 +(0..255)
OffsetY= Oy-BOy*256 +(0..255)
a) Cuadrante 1 (Correspondiente a la figura de arriba) 0 <= ø < 90 | HAD=x11
cos(ø)*radio=(255-OffsetX) ===> radio=(255-OffsetX)/cos(ø) |___
El rayo saldrá por el lado horizontal derecho si:
sen(ø)*radio < (255-OffsetY) ===> (255-OffsetX)*Tg(ø)-(255-OffsetY) < 0 ...si no, sale por abajo.
b) Cuadrante 2 90 <= ø < 180
-cos(ø)*radio=OffsetX ===> radio=-OffsetX/cos(ø) | HAD=x10
El rayo saldrá por el lado horizontal izquierdo si: ___|
sen(ø)*radio < (255-OffsetY) ===> -OffsetX*Tg(ø)-(255-OffsetY) < 0 ... si no, sale por abajo
___
c) Cuadrante 3 180 <= ø < 270 | HAD=x00
-cos(ø)*radio=OffsetX ===> radio=-OffsetX/cos(ø) |
El rayo saldrá por el lado horizontal izquierdo si:
-sen(ø)*radio < OffsetY ===> OffsetX*Tg(ø)-OffsetY < 0 ...si no, sale por arriba.
___
d) Cuadrante 4 270 <= ø < 360 |
cos(ø)*radio=OffsetX ===> radio=(256-OffsetX)/cos(ø) | HAD=x01
El rayo saldrá por el lado horizontal derecho si:
-sen(ø)*radio < OffsetY ===>-(255-OffsetX)*Tg(ø)-OffsetY < 0 ... si no, sale por arriba
Ahora ya sabemos cual de los dos caminos tenemos que coger. Debemos hallar ahora una forma de recorrer
todos los cuadros hasta chocar con algo.
En los sectores 1,4,5,8, |Tg(ø)| es menor o igual a 1. La Tg se puede interpretar como: (toma
carrerilla...) "lo que hay que aumentar la y, por cada unidad que aumente la x".
Asimismo, la Cotg es "lo que hay que aumentar la x, por cada unidad de y que añada".
En los sectores 2,3,6,7, es la |Cotg(ø)| la que es menor o igual que 1.
Que en los octantes la |Tg| o la |Cotg| sea menor que uno, es importante, ya que queremos trazar un
camino "continuo". Si fuese mayor que uno, nos saltaríamos algun bloque sin testear. Si no entiendes
ni "j", mira los parrafos siguientes.
*) Supongamos que nuestro ángulo es 1,4,5 u 8. Lo que debemos hacer, es incrementar
(o decrementar) la x de uno en uno, y por cada vez que lo hagamos, aumentaremos la y por el valor de
Tg(ø). Además, deberemos hacerlo de una forma alternativa, es decir:
a) x=x±1; TestearBloque (x,y) (+1 si 1 u 8, y -1 si 4 o 5)
b) y=y±Tg(ø); TestearBloque (x,y) (+ si 1 u 8, y - si 4 o 5)
c) x=x±1; TestearBloque (x,y)
d) y=y±Tg(ø); TestearBloque (x,y)
.
.
. seguir asi hasta que el test resulte positivo (mas de 0.6, multa por embriaguez, je,je,je).
Eso en el caso de que el rayo saliese del (BOx,BOy) por una pared horizontal. Si el rayo saliese
por una pared vertical, deberiamos añadir un paso inicial:
0) y=y±Tg(ø); TestearBloque (x,y)
*) Si el ángulo es 2,3,6,7...
a) y=y±1; TestearBloque (x,y) (+1 si 2 o 3, y -1 si 6 o 7)
b) x=x±Cotg(ø); TestearBloque (x,y) (+ si 2 o 3, - si 6 o 7)
c) y=y±1; TestearBloque (x,y)
d) x=x±Cotg(ø): TestearBloque (x,y)
.
.
. seguir asi hasta chocar.
Eso si el rayo saliese de (BOx,BOy) por una pared vertical. Si saliese horizontalmente, habría que
añadir un nuevo paso al principio:
0) x=x±Cotg(ø); TestearBloque (x,y)
Resumiendo...
|
\ Cot(ß)|Cot(ß) / ðx
\ 1 | 1 / ðy
-1 \ | / 1
-Tg(ß) \ | / Tg(ß)
\|/
180°-------------+------------- 0°
-1 /|\ 1
-Tg(ß) / | \ Tg(ß)
/ | \
/ | \
/ -Cot(ß)|-Cot(ß) \
-1 | -1
Tiene pintas de ser rapido. La Tg y Cotg es la misma para todos los pasos
de un mismo rayo, y no hay multiplicaciones ni divisiones. Ya veremos...
Nota: TestearBloque, entre otras cosas, debería anotar en algún sitio la dirección de choque, y
la x e y del bloque de choque, para cálculos posteriores.
**************
*PSEUDOCODIGO*
**************
TrazarRayo(µ,&Result)
¯¯¯¯¯¯¯¯¯¯
HAD=HallarHAD(µ);
BOx=Ox/256;
BOy=Oy/256;
OffsetX= Ox-BOx*256
OffsetY= Oy-BOy*256
CASE µ OF
CASE PrimerCuadrante entonces /*HAD=x11*/
Si ((255-OffsetX)*Tg(µ)-(255-OffsetY) < 0) entonces salida=horizontal
sino salida=vertical
CASE SegundoCuadrante entonces /*HAD=x10*/
Si (-OffsetX*Tg(µ)-(255-OffsetY) < 0) entonces salida=horizontal
sino salida=vertical
CASE TercerCuadrante entonces /*HAD=x00*/
Si (OffsetX*Tg(µ)-OffsetY < 0) entonces salida=horizontal
sino salida=vertical
CASE CuartoCuadrante entonces /*HAD=x01*/
Si (-(255-OffsetX)*Tg(µ)-OffsetY < 0) entonces salida=horizontal
sino salida=vertical
END_CASE
Si (octante=1 ó octante=4 ó octante=5 ó octante=8) entonces /*HAD=1xx*/
{
Si (salida=vertical) entonces
{
BOy=BOy+Tg(µ)±1; dependiendo del ángulo
Si (Mapa(BOx,BOy)<>0) entonces
{
Si Tg(µ)>0 entonces *Result.Direc=arriba
sino *Result.Direc=abajo
GOTO CHOQUE
}
}
Si (octante=1 ó octante=8) entonces ðBx=1 /*HAD=xx1)*/
sino ðBx=-1
DO_WHILE 1=1
BOx=BOx+ðBx;
Si (Mapa(BOx,BOy)<>0) entonces
{
Si ðBx=1 entonces *Result.Direc=derecha
sino *Result.Direc=izquierda
GOTO CHOQUE
}
BOy=BOy+Tg(µ);
Si (Mapa(BOx,BOy)<>0) entonces
{
Si Tg(µ)>0 entonces *Result.Direc=arriba
sino *Result.Direc=abajo
GOTO CHOQUE
}
END_WHILE
}
sino
{
Si salida=horizontal entonces
{
BOx=BOx+Cotg(µ)±1; dependiendo del ángulo
Si (Mapa(BOx,BOy)<>0) entonces
{
Si Cotg(µ)>0 entonces *Result.Direc=derecha
sino *Result.Direc=izquierda
GOTO CHOQUE
}
}
Si (octante=2 ó octante=3) entonces ðBy=1 /*HAD=x1x*/
sino ðBy=-1
DO_WHILE 1=1
BOy=BOy+ðBy;
Si (Mapa(BOx,BOy)<>0) entonces
{
Si ðBy=1 entonces *Result.Direc=arriba
sino *Result.Direc=abajo
GOTO CHOQUE
}
BOx=BOx+Cotg(µ);
Si (Mapa(BOx,BOy)<>0) entonces
{
Si Cotg(µ)>0 entonces *Result.Direc=derecha
sino *Result.Direc=izquierda
GOTO CHOQUE
}
END_WHILE
}
CHOQUE: *Result.Bloque=(BOx,BOy);
COMO PROYECTAR UN RAYO BUSCADOR
===============================
Ya sabemos trazar un rayo generico. Veamos ahora como trazar un RayoBuscador, que dado un angulo,
nos devuelva, ademas de el Bloque y la dirección de choque, el offset de la textura y la distancia
del observador a el punto de impacto (Z).
a) Si el rayo ha chocado hacia arriba...
i) 90 < ø < 180 ############## Cotg(ø)= -
Dy=(By*256)-Oy |\ Dy= +
Dx=Dy*Cotg(ø) | \ Dx= + * - = -
Dy | \__ Cosec(ø)= +
| \ \ ø
|....*.\.....
xxxxxxx Dx
DTextur=Ox-(Bx*256)+Dx DTextur= desplazamiento dentro de Textura
Z=Dy*Cosec(ø) Z= distancia del observador al punto de choque
ii) 0 < ø < 90 Cotg(ø)= +
Dy=(By*256)-Oy Dy=+
Dx=Dy*Cotg(ø) Dx= + * + = +
Cosec(ø)= +
DTextur=Ox-(Bx*256)+Dx
Z=Dy*Cosec(ø)
Se puede ver que no hay que diferenciar en octantes, ya que la expresión coincide. Es decir; si
hemos chocado hacia arriba, nos da igual en que cuadrante esté el ángulo.
b) Si el rayo ha chocado hacia abajo...
Dy=Oy-((By+1)*256) Dy=+
Dx=Dy*Cotg(ø)
DTextur=((Bx+1)*256)-Ox+Dx
Z=-Dy*Cosec(ø)
c) Si el rayo ha chocado hacia la derecha...
Dx=(Bx*256)-Ox Dx=+
Dy=Dx*Tg(ø)
DTextur=((By+1)*256)-Oy+Dy
Z=Dy*Cosec(ø)
d) Si el rayo ha chocado hacia la izquierda...
Dx=Ox-((Bx+1)*256) Dx=+
Dy=Dx*Tg(ø)
DTextur=-(By*256)-Oy+Dy
Z=-Dy*Cosec(ø)
**************
*PSEUDOCODIGO*
**************
TrazaBuscador (µ)
¯¯¯¯¯¯¯¯¯¯¯¯¯
Pared[Punt].ßInicial=µ; /*Esta pared,SEGURO que empieza en este ángulo*/
TrazarRayo(µ,&Pared[Punt]);
CASE Pared[Punt].Direc OF
CASE Arriba entonces
{
Dy=(Pared[Punt].By*256)-Oy;
Dx=Dy*Cotg(µ);
Pared[Punt].InicioTextura=Ox-(Pared[Punt].Bx*256)+Dx;
Pared[Punt].ZInicial=Dy*Cosec(µ);
}
CASE Abajo entonces
{
Dy=((Pared[Punt].By+1)*256)-Oy;
Dx=Dy*Cotg(µ);
Pared[Punt].InicioTextura=((Pared[Punt].Bx+1)*256)-Ox+Dx;
Pared[Punt].ZInicial=Dy*Cosec(µ);
}
CASE Izquierda entonces
{
Dx=((Pared[Punt].Bx+1)*256)-Ox;
Dy=Dx*Tg(µ);
Pared[Punt].InicioTextura=Oy-(Pared[Punt].By*256)+Dy;
Pared[Punt].ZInicial=Dx*Sec(µ)
}
CASE SI_OTRO entonces /*derecha*/
{
Dx=(Pared[Punt].Bx*256)-Ox
Dy=Dx*Tg(µ);
Pared[Punt].InicioTextura=((Pared[Punt].By+1)*256)-Oy+Dy;
Pared[Punt].ZInicial=Dx*Sec(µ);
}
END_CASE
COMO PROYECTAR UN RAYO VERIFICADOR
==================================
Dado el observador, un bloque y una dirección, verifica si el rayo que une el observador, con el
final de la pared correspondiente, choca realmente con dicha pared, o si hay algún obstaculo delante.
a) Si choca con la misma pared, devolverá el ß correspondiente a la esquina.
b) Si choca con otra distinta de la pared esperada, realizará una llamada recursiva con la
nueva pared, y finalmente devolvera el último ß.
Esta funcion recibe un nodo de pared parcialmente actualizado:
(ßInicial,Bloque,Direc,InicioTextura,ZInicial)
**************
*PSEUDOCODIGO*
**************
ángulo TrazaVerificador (&ParedMedia)
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
pila Buffer;
{
ComputaAngFinal(*ParedMedia.Bloque,*ParedMedia.Direc,&µ); /*ø=ángulo de esquina final de pared*/
Si µ>ø+Angß entonces µ=ø+Angß
TrazarRayo(µ,&Buffer);
Si Buffer.Bloque=*ParedMediaBloque entonces
{
*ParedMedia.ßFinal=µ;
Punt++; /*y SEGURO que pasaremos a la siguiente*/
devuelve(µ); /*devuelve el último ß escaneado*/
}
sino {
InicPar=ComputaAngInicio(Pared[Punt].Bloque,Pared[Punt].Direc);
*ParedMedia.ßFinal=InicPar-ðß;
Punt++;
Pared[Punt].ßInicial=InicPar;
Pared[Punt].InicioTextura=0;
Pared[Punt].Bloque=Buffer.Bloque;
Pared[Punt].Direc=Buffer.Direc;
Pared[Punt].ZInicial=;
ç=TrazaVerificador(&Pared[Punt]);
devuelve(ç);
}
/*Termina de Actualizar pared comenzada*/
CASE *ParedMedia.Direc OF
CASE Arriba entonces
{
Dy=(*ParedMedia.By*256)-Oy;
Dx=Dy*Cotg(µ);
*ParedMedia.FinalTextura=Ox-(*ParedMedia.Bx*256)+Dx;
*ParedMedia.ZFinal=Dy*Cosec(µ);
}
CASE Abajo entonces
{
Dy=((*ParedMedia.By+1)*256)-Oy;
Dx=Dy*Cotg(µ);
*ParedMedia.FinalTextura=((*ParedMedia.Bx+1)*256)-Ox+Dx;
*ParedMedia.ZFinal=Dy*Cosec(µ);
}
CASE Izquierda entonces
{
Dx=((*ParedMedia.Bx+1)*256)-Ox;
Dy=Dx*Tg(µ);
*ParedMedia.FinalTextura=Oy-(*ParedMedia.By*256)+Dy;
*ParedMedia.ZFinal=Dx*Sec(µ)
}
CASE SI_OTRO entonces /*derecha*/
{
Dx=(*ParedMedia.Bx*256)-Ox
Dy=Dx*Tg(µ);
*ParedMedia.FinalTextura=((*ParedMedia.By+1)*256)-Oy+Dy;
*ParedMedia.ZFinal=Dx*Sec(µ);
}
END_CASE
}
Refinemos la funcion que, dado un bloque, el observador, y una direccion, nos devuelve el angulo
formado por el rayo que "une" el observador, con el final de la pared impactada.
NOTA: El angulo no sera uno cualquiera, sino uno de la serie ß.
**************
*PSEUDOCODIGO*
**************
ComputaAngFinal (Bloque,Direc,&angulo)
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Case Direc of
Case Arriba entonces {
OffsetX=(Bx+1)*256-Ox
OffsetY=By*256-Oy
}
Case Abajo entonces {
OffsetX=Bx*256-Ox
OffsetY=(By+1)*256-Oy
}
Case Izquierda entonces {
OffsetX=(Bx+1)*256-Ox
OffsetY=(By+1)*256-Oy
}
Case Derecha entonces {
OffsetX=Bx*256-Ox
OffsetY=By*256-Oy
}
End_Case
Si OffsetX <> 0 entonces *angulo=arctg(OffsetY/OffsetX)
sino Si OffsetY>0 entonces *angulo=90°
sino *angulo=270°
Ahora refinemos la funcion que, dado un bloque, el observador, y una direccion, nos devuelve el angulo
formado por el rayo que "une" el observador, con el principio de la pared impactada.
NOTA: El ángulo devuelto pertenecera a la serie ß.
**************
*PSEUDOCODIGO*
**************
angulo ComputaAngInicio (Bloque,Direc,&angulo)
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Case Direc of
Case Arriba entonces {
OffsetX=Ox-Bx*256
OffsetY=Oy-By*256
}
Case Abajo entonces {
OffsetX=Ox-(Bx+1)*256
OffsetY=Oy-(By+1)*256
}
Case Izquierda entonces {
OffsetX=Ox-(Bx+1)*256
OffsetY=Oy-By*256
}
Case Derecha entonces {
OffsetX=Ox-Bx*256
OffsetY=Oy-(By+1)*256
}
End_Case
Si OffsetX <> 0 entonces *angulo=arctg(OffsetY/OffsetX)
sino Si OffsetY>0 entonces *angulo=90°
sino *angulo=270°
Y como ya GOBLINS ha entrado en la frenetica dinamica de acabar la demo, esto va a ser lo ultimo que
escriba sobre el tema. No esta todo, y sobran muchas cosas.En realidad el algoritmo implementado dista
mucho de este doc, debido a las multiples modificaciones y optimizaciones, pero deberia servir para
que cualquiera que se lo proponga, pueda comenzar a fabricar su propio algoritmo. Repito que este doc
esta lleno de fallos y redundancias, y no es en absoluto implementable directamente. No os lo voy a
dar todo ya digerido, hombres!. Pero pistas, teneis un monton, y seguro que mejoras tambien. Las
mejoras que encontreis, ya me las contareis, vale?..
Bueno, ahora voy a ponerme a hacer el suelo mapeado... un saludillo...
PHORNEE/GOBLINS
Ah..!!.. y atentos a la proxima demo de GOBLINS, que se va a presentar en la Euskal Amiga Party III,
y que se va a titular...
..."EL OCASO DE UN PAYASO" (The down of the clown).... nos veremos alli...
PD: Cago en dios, tenemos que dejar el pabellon de AMIGA bien alto, joder, que van a ir peceros que
controlan la ostia... a currar TODOS tios...